﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace BattleThis.Core.Movement
{
	/// <summary>
	/// Service that supports unit moving.
	/// </summary>
	public class MoveActionService : BattleThis.Core.Movement.IMoveActionService
	{

		#region Variablen

		#endregion

		#region Properties
		/// <summary>
		/// Gets the map of the game.
		/// </summary>
		public Map Map { get; private set; }
		#endregion

		#region Methoden

		/// <summary>
		/// Initializes a new instance of the <see cref="MoveActionService"/> class.
		/// </summary>
		/// <param name="map">The map.</param>
		public MoveActionService( Map map )
		{
			if( map == null ) throw new ArgumentNullException( "map" );

			this.Map = map;
		}

		/// <summary>
		/// Moves the unit on the specified field to the specified position.
		/// </summary>
		/// <param name="selected">The unit on the selected field.</param>
		/// <param name="to">The position to move.</param>
		public void Move( Field selected, Position to )
		{
			if( selected == null )throw new ArgumentNullException( "selected" );
			if( to == null )throw new ArgumentNullException( "to" );

			if( selected.Unit.IsEmpty() ) return;

			var exploration = this.Explore( selected );

			if( !exploration.Contains( to ) ) return;
			if( !this.Map.Fields[ to ].Unit.IsEmpty() ) return;

			this.Map.Fields[ to ].Unit = selected.Unit;

			selected.Unit.ActionPoints = exploration.Find( to ).Remaining;
			selected.Unit = Unit.Empty;

		}

		/// <summary>
		/// Gets the possible range of a unit on this map.
		/// </summary>
		/// <param name="unit">The unit to get the range from.</param>
		/// <returns></returns>
		public IEnumerable<Position> GetRange( Unit unit )
		{
			if( unit == null )throw new ArgumentNullException( "unit" );
			if( unit.IsEmpty() ) throw new ArgumentOutOfRangeException( "unit is empty" );

			var unitField = this.Map.Find( unit );

			return Explore( unitField ).ToArray();

		}

		/// <summary>
		/// Explores the possible path for the specified unit.
		/// </summary>
		/// <param name="unit">The unit to get the path for.</param>
		/// <returns>A PathCollection the unit can go.</returns>
		protected PathCollection Explore( Field unitField )
		{
			if( unitField == null ) throw new ArgumentNullException( "unitPosition" );

			var rootPath = new Path( unitField.Position , unitField.Unit.ActionPoints );
			var found = new PathCollection();

			this.Explore( rootPath, found );

			return found;
		}

		/// <summary>
		/// Explores recrusive the path alonge the orign.
		/// </summary>
		/// <param name="orgin">The orgin.</param>
		/// <param name="explored">The already explored paths.</param>
		protected void Explore( Path orgin , PathCollection explored )
		{
			if( orgin == null )throw new ArgumentNullException( "orgin" );
			if( explored == null )throw new ArgumentNullException( "explored" );

			var orginField = this.Map.Fields[ orgin.Target ];
			var neighbours = this.Map.GetNeighbours( orgin.Target );

			foreach( var neighbour in neighbours )
			{
				var neighbourField =  this.Map.Fields[ neighbour ];
				var remainingPoints = orgin.Remaining.Subtract( this.Map.Fields[ neighbour ] );

				if( remainingPoints.IsWasted() ) continue;

				var path = new Path( neighbour, orgin, remainingPoints );

				if( explored.Find( neighbour ).IsCheaperAs( path ) )
					continue;

				explored.Add( path );
				this.Explore( path, explored );
			}

		}

		#endregion

	}
}